package jaspect.model;


import jaspect.control.ConfigurationBuilder;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Properties;
import javax.xml.parsers.ParserConfigurationException;
import org.xml.sax.SAXException;

/**
 *
 * @author Abin Baby
 */
public class AJCompiler {
//Singleton design pattern

    private List<Exception> errors = new LinkedList<Exception>();
    private static AJCompiler compiler = new AJCompiler();

    /**
     *
     * @return
     */
    public static AJCompiler getInstance() {
        return compiler;
    }

    private AJCompiler() {
    }
    //end pattern

    
    private StringBuilder inputJava;
    private StringBuilder outputJava;


    /**
     *
     * @return
     */
    public StringBuilder getInputJava() {
        return inputJava;
    }

    /**
     *
     * @param InputJava
     */
    public void setInputJava(StringBuilder InputJava) {
        this.inputJava = InputJava;
    }
    private String xmlPath;
    private String SourcePath;
    String OutputPath;

    /**
     * Read the source program and corresponding to each #db statement replaced by proper java syntax
     * @param cb
     */
    public void readSource(ConfigurationBuilder cb) {
        BufferedReader in = null;
        inputJava = new StringBuilder();
        try {

            System.out.println(getSourcePath());
            //Opens the file

            FileInputStream fstream = new FileInputStream(this.getSourcePath());
            in = new BufferedReader(new InputStreamReader(fstream));
            String str = in.readLine();
            //Reads from the file
            while (str != null) {
                inputJava.append(str);
                inputJava.append("\n");
                str = in.readLine();
            }

        } catch (IOException ex) {
            throw new RuntimeException("Could not open source file: " + ex.getMessage());
        } finally {
            if (in != null) {
                try {
                    in.close();
                } catch (IOException ex) {
                }
            }
        }

    }

    /**
     *
     * @return
     */
    public String getXmlPath() {

        //  String temp = null;
        String curDir = System.getProperty("user.dir");

        curDir += "\\";
        curDir += xmlPath;

        System.out.println(curDir);




        return curDir;
    }

    /**
     * create a list of all the aspect code
     * @return
     */
    public List<String> extractAspects() {
        if (inputJava.length() == 0) {
            throw new RuntimeException("Source has not been read!");
        }
        StringBuilder sb = new StringBuilder(inputJava.toString());

        int start = 0;
        int end = 0;

        List<String> aspectList = new ArrayList<String>();

        while (true) {
            start = inputJava.indexOf("#", start);
            if (start == -1) {
                break;
            }
            end = inputJava.indexOf("#", start + 1);
            if (end == -1) {
                throw new RuntimeException("Malformed source! (No matching end marker)");
            }
            String aspect = inputJava.substring(start + 1, end);
            aspectList.add(aspect);
            inputJava.delete(start, end + 1);
//            start = end + 1;
            if (inputJava.length() <= start) {
                break;
            }
        }
        setOutputJava(inputJava);
        inputJava = sb;
        return aspectList;
    }

    /**
     * generates the final java code as an output file
     * @param cb 
     * @param aspectList
     */
    public void generateJava(ConfigurationBuilder cb, List<String> aspectList) {
        CodeGenerator cg = new CodeGenerator();
        cg.setJavaSource(getOutputJava());

        for (String aspectCode : aspectList) {
            String aspectName = aspectCode.substring(0, aspectCode.indexOf("\n"));
            AspectConfiguration t = cb.getAspectConfiguration(aspectName);
            if (t == null) {
                throw new RuntimeException("Missing aspect configuration '" + aspectName + "' !");
            }
            if (t.getMode() == AspectConfiguration.InterceptMode.member) {
                cg.generateJavaForMember(aspectCode, t);
            } else {
                cg.generateJavaForMethod(aspectCode, t);
            }

        }

    }

    /**
     * It converts the AspectList to corresponding java code
     * @param AspectList
     * @return
     */
    public List toSourceString(List AspectList) {
        List<String> AspectString = new ArrayList();
        Iterator i = AspectString.iterator();
        while (i.hasNext()) {
            //System.out.println(i.next());

        }
        return AspectString;


    }

    /**
     * This main is used to deploy the application independent of the interface
     * the program works fine even without an interface  TOO SMART  :-)
     *
     * @param args
     */
    public static void main(String args[]) {
       
      
        if (args.length == 0) {
            throw new UnsupportedOperationException("No input file;\nUsage: java jaspect.model.AJCompiler aj_source_file_name\n");
        }
        ConfigurationBuilder cb = new ConfigurationBuilder();
        try {
            getInstance().errors.clear();
            cb.buildConfigurations(args[1]);
            AJCompiler ac = getInstance();
            ac.setSourcePath(args[0]);
            ac.setXmlPath(args[1]);
            ac.readSource(cb);
            List<String> asps = ac.extractAspects();
            ac.generateJava(cb, asps);
           


        } catch (ParserConfigurationException ex) {
            getInstance().errors.add(ex);
            getInstance().outputJava = null;
            throw new RuntimeException("Could not parse the configuration: " + ex.getMessage());
        } catch (IOException ex) {
            getInstance().errors.add(ex);
            throw new RuntimeException("Could not open the file: " + ex.getMessage());
        } catch (SAXException ex) {
            getInstance().errors.add(ex);
            throw new RuntimeException("Could not parse the configuration: " + ex.getMessage());
        }
    }

    /**
     * @return the SourcePath
     */
    public String getSourcePath() {
        String curDir = System.getProperty("user.dir");
        return SourcePath;
    }

    /**
     * @param SourcePath the SourcePath to set
     */
    public void setSourcePath(String SourcePath) {
        this.SourcePath = SourcePath;
    }

    /**
     *
     * @param temp
     */
    public void setXmlPath(String temp) {
        xmlPath = temp;

    }

    /**
     * @return the outputJava
     */
    public StringBuilder getOutputJava() {
        return outputJava;
    }

    /**
     * @param outputJava the outputJava to set
     */
    public void setOutputJava(StringBuilder outputJava) {
        this.outputJava = outputJava;
    }

    /**
     * @return the errors
     */
    public List<Exception> getErrors() {
        return errors;
    }
}
